/* 
 * File:   XUDPShareMem.cpp
 * Author: minaev
 * 
 * Created on 5 Ноябрь 2013 г., 15:50
 */

#include "XUDPShareMem.h"
#include "Xservice.h"
#include "XUDPScaner.h"

#include <stdio.h>

XUDPShareMem::XUDPShareMem(XUDPServ* serv, XshareMem* shrmem) {
    this->serv = serv;
    this->shrmem = shrmem;
}

XUDPShareMem::~XUDPShareMem() {
}

char* XUDPShareMem::packReadReq(const char* buf, uint16_t len) {
    return this->formPack(UC_READ | 1, *(uint16_t*) (buf + OFST_NLINE),
            *(uint16_t*) (buf + OFST_NBLOCK),
            *(uint16_t*) (buf + OFST_CBLOCK));
}

char* XUDPShareMem::packWriteReq(const char* buf, uint16_t len) {
    return this->formPack(UC_WRITE | 1, *(uint16_t*) (buf + OFST_NLINE),
            *(uint16_t*) (buf + OFST_NBLOCK),
            *(uint16_t*) (buf + OFST_CBLOCK));
}

/**
 * @param ip - if 0xffffffff then broadcast
 * @param cmd
 * @param nLine
 * @param nBlock
 * @param cntBlocks
 * @return 0 - success, else error
 */
int XUDPShareMem::sendPack(uint32_t ip, uint8_t cmd, uint16_t nLine, uint16_t nBlock, uint16_t cntBlocks) {
    static char* buf;
    static int res;
    static uint16_t cntololo;
    buf = formPack(cmd, nLine, nBlock, cntBlocks);
    if (buf != NULL) {
        res = this->serv->sendMsg(ip, DST_UDP_PORT, buf, XUDPScaner::getLengthInPack(buf));
        if (ip == 0xffffffff) {
            cntololo = this->shrmem->getLineLength(SLN_XOLODEV);
            for (int i = 0; i < cntololo; i++) {
                res |= this->serv->sendMsg(
                        *(uint32_t*)this->shrmem->read(SLN_XOLODEV, i),
                        DST_UDP_PORT, buf, XUDPScaner::getLengthInPack(buf));
            }
        }
        return res;
    } else {
        Xmsg::printf("Error in forming pack for send cmd: %i, nLine %i, nBlock %i, cntBlock %i\n",
                cmd, nLine, nBlock, cntBlocks);
    }
    return 1;
}

char* XUDPShareMem::formPack(uint8_t cmd, uint16_t nLine, uint16_t nBlock, uint16_t cntBlocks) {
    //pthread_mutex_lock(&this->assccFormPack);
    static char* sbuf = NULL;
    static uint32_t sbuflen;
    static uint16_t crc;
    if (sbuf != NULL) {
        delete [] sbuf;
    }
    if (nLine >= shrmem->getCountLines()) {
        Xmsg::printf("error formPack, must be: nLine %i < getCountLines %i \n",
                nLine, shrmem->getCountLines());
        sbuf = NULL;
        return NULL;
    }
    if ((nBlock + cntBlocks) > shrmem->getLineLength(nLine)) {
        Xmsg::printf("error formPack, must be: nBlock + cntBlocks %i <= getLineLength %i \n",
                nBlock + cntBlocks, shrmem->getLineLength(nLine));
        sbuf = NULL;
        return NULL;
    }
    sbuflen = cntBlocks * shrmem->getLineBlockLength(nLine) + SUM_WITHOUT_DATA;
    sbuf = new char [sbuflen];

    this->setMagLenBuf(sbuf, sbuflen);

    this->setShareFieldsBuf(sbuf, sbuflen, cmd,
            nLine, nBlock, cntBlocks);

    //block
    memcpy((void*) (sbuf + OFST_DATA), shrmem->read(nLine, nBlock),
            shrmem->getLineBlockLength(nLine) * cntBlocks);
    //crc
    memcpy((void*) (sbuf + sbuflen - 2), &(crc = XUDPShareMem::getCRC(sbuf, sbuflen)), 2);

    return sbuf;
}

void XUDPShareMem::setMagLenBuf(char* buf, uint32_t len) {
    if (len >= SUM_WITHOUT_DATA) {
        //magic number
        buf[0] = (MAGIC_NUM >> 8) & 0xff;
        buf[1] = MAGIC_NUM & 0xff;
        //length without magic number, length and crc
        buf[2] = ((len - SUM_WITHOUT_PACK) >> 8) & 0xff;
        buf[3] = (len - SUM_WITHOUT_PACK) & 0xff;
    }
}

void XUDPShareMem::setShareFieldsBuf(char* buf, uint32_t len, uint8_t cmd,
        uint16_t nLine, uint16_t nBlock, uint16_t ncntBlock) {
    //cmd
    buf[OFST_CMD] = cmd;
    //nLine
    memcpy((void*) (buf + OFST_NLINE), &nLine, 2);
    //nBlock
    memcpy((void*) (buf + OFST_NBLOCK), &nBlock, 2);
    //ncntBlock
    memcpy((void*) (buf + OFST_CBLOCK), &ncntBlock, 2);

}

void XUDPShareMem::setFieldsBuf(char* buf, uint32_t len, uint8_t cmd,
        uint16_t nLine, uint16_t nBlock, uint16_t ncntBlock, const void* data) {
    this->setMagLenBuf(buf, len);
    this->setShareFieldsBuf(buf, len, cmd, nLine, nBlock, ncntBlock);
    memcpy(buf + OFST_DATA, data, len - SUM_WITHOUT_DATA);
}

/**
 * 
 * @param buf
 * @param len
 * @return 0 - success, else error
 */
int XUDPShareMem::unpackWriteMem(const char* buf, int16_t len) {
    return this->shrmem->write((void*) (buf + OFST_DATA), *(uint16_t*) (buf + OFST_NLINE),
            *(uint16_t*) (buf + OFST_NBLOCK), *(uint16_t*) (buf + OFST_CBLOCK));
}

/**
 * Processing input buffers (UDP packets)
 * !!!replace to constant PROC ERROR CODES
 * @param buf - buffer for processing
 * @param len - length of buffer
 * @return 0 - success, else error
 */
int XUDPShareMem::processBuf(const char* buf, int16_t len, uint32_t ip) {
    static char* sbuf;
    Xmsg::printf("UDP SHARE MEM: processed.\n");
    switch (*(uint8_t*) (buf + OFST_CMD)) {
        case UC_READ:
        {
            Xmsg::printf("UDP SHARE MEM: UC_READ\n");
            sbuf = packReadReq(buf, len);
            if (sbuf != NULL) {
                this->serv->sendMsg(ip, DST_UDP_PORT, (void*) sbuf,
                        XUDPScaner::getLengthInPack(sbuf));
                //Xmsg::printf("sended to 0x%x\n",ip);
            }
            //this->shrmem->printMem();
            //Xmsg::printf("RECEIVED!!!!!!\n");
            break;
        }
        case UC_WRITE:
        {
            Xmsg::printf("UDP SHARE MEM: UC_WRITE\n");
            if (unpackWriteMem(buf, len) == 0) {
                sbuf = packWriteReq(buf, len);
                this->serv->sendMsg(ip, DST_UDP_PORT, (void*) sbuf, XUDPScaner::getLengthInPack(sbuf));
                this->shrmem->printMem();
            }
            break;
        }
        case UC_BROADCAST:
        {
            Xmsg::printf("UDP SHARE MEM: UC_BROADCAST\n");
            if (this->sendPack(0xffffffff, UC_BROADCAST | 1, SLN_XCOUNT, 0, 1) != 0) {
                Xmsg::printf("Error in send broadcast UDP!\n");
            }
            break;
        }
        case UC_ADD:
        {
            Xmsg::printf("UDP SHARE MEM: UC_ADD\n");
            bool added = false;
            uint16_t num_added = 0;
            if (*(uint16_t*) (buf + OFST_CBLOCK) == 0) {
                added = this->shrmem->addLineBlockWithoutRepeate(buf + OFST_DATA,
                        *(uint16_t*) (buf + OFST_NLINE));
            } else {
                added = this->shrmem->addLineBlock(buf + OFST_DATA,
                        *(uint16_t*) (buf + OFST_NLINE));
            }

            if (added) {
                num_added = shrmem->getLineLength(
                        *(uint16_t*) (buf + OFST_NLINE)
                        ) - 1;
            }

            if (this->sendPack(ip, UC_ADD | 1,
                    *(uint16_t*) (buf + OFST_NLINE), num_added,
                    added ? 1 : 0) != 0) {
                Xmsg::printf("Error in send added UDP!\n");
            }

            break;
        }
        case UC_REMOVE:
        {
            Xmsg::printf("UDP SHARE MEM: UC_REMOVE\n");
            this->shrmem->removeLineBlock((uint8_t*) (buf + OFST_DATA),
                    *(uint16_t*) (buf + OFST_NLINE));

            if (this->sendPack(ip, UC_REMOVE | 1,
                    *(uint16_t*) (buf + OFST_NLINE), 0, 0) != 0) {
                Xmsg::printf("Error in send removed UDP!\n");
            }
            break;
        }
        case UC_LENLINE:
        {
            Xmsg::printf("UDP SHARE MEM: UC_LENLINE\n");
            sbuf = new char[SUM_WITHOUT_DATA + 2];
            uint16_t nline = shrmem->getLineLength(
                    *(uint16_t*) (buf + OFST_NLINE));
            setFieldsBuf(sbuf, SUM_WITHOUT_DATA + 2, UC_LENLINE | 1,
                    nline, 0, 0, &nline);
            
            this->serv->sendMsg(ip,DST_UDP_PORT,sbuf,SUM_WITHOUT_DATA+2);
            
            break;
        }
        case UC_ADD_DEV:
        {
            Xmsg::printf("UDP SHARE MEM: UC_ADD_DEV\n");
            int num_added = this->shrmem->addLineBlockWithoutRepeate(&ip, SLN_XOLODEV);

            if (this->sendPack(ip, UC_ADD_DEV | 1,
                    SLN_XOLODEV, num_added, 1) != 0) {
                Xmsg::printf("Error in send added dev UDP!\n");
            }

            break;
        }
        case UC_REM_DEV:
        {
            Xmsg::printf("UDP SHARE MEM: UC_REM_DEV\n");
            this->shrmem->removeLineBlock(&ip, SLN_XOLODEV);

            if (this->sendPack(ip, UC_REM_DEV | 1,
                    SLN_XOLODEV, 0, 0) != 0) {
                Xmsg::printf("Error in send removed dev UDP!\n");
            }
            break;
        }
        case UC_LENMEM:
        {
            Xmsg::printf("UDP SHARE MEM: UC_LENMEM\n");
            sbuf = new char[SUM_WITHOUT_DATA + 2];
            uint16_t cntlines = shrmem->getCountLines();
            setFieldsBuf(sbuf, SUM_WITHOUT_DATA + 2, UC_LENMEM | 1,
                    0, 0, 0, &cntlines);
            
            this->serv->sendMsg(ip,DST_UDP_PORT,sbuf,SUM_WITHOUT_DATA+2);
            
            break;
        }
        default:
        {
            Xmsg::printf("UDP SHARE MEM: XEPH9 = 0x%x\n", *(uint8_t*) (buf + OFST_CMD));
        }
    }

    //this->shrmem->printMem();
}

uint16_t XUDPShareMem::getCRC(char* buf, uint32_t len) {
    static uint16_t crc;
    crc = 0;
    for (uint32_t i = 2; i < len - 3; i += 2) {
        crc ^= *(uint16_t*) (buf + i);
    }
    if (len % 2 == 1) {
        crc ^= (*(uint8_t*) (buf + len - 3));
    }
    crc = 0xFFFF - crc;
    return crc;
}



